package coevolution;
import genome.GGT;
import genome.Genotype;
import tool.Utility;

import java.util.ArrayList;
import java.util.Iterator;
import java.util.Vector;

public class IPCA {
	
	public static ArrayList<Genotype> CSt = new ArrayList<Genotype>();
	public static ArrayList<Genotype> TSt = new ArrayList<Genotype>();
	
	public static int submit(ArrayList<Genotype> Cnew,ArrayList<Genotype> Tnew,int t){
		
		ArrayList<Genotype> CStp1 = Utility.copy(CSt);
		ArrayList<Genotype> TStp1 = Utility.copy(TSt);
		
		for(Genotype C : Cnew){
			Vector v = findUseful(C,CStp1,TStp1,Tnew);
			boolean useful = (Boolean)v.get(0);
			ArrayList<Genotype> Tsel = (ArrayList<Genotype>)v.get(1);
			
			if(useful){
				CStp1 = Utility.union(CStp1,C);
				
				ArrayList<Genotype> CDom = dominated(C,CStp1,TStp1); 
				CStp1 = Utility.remove(CStp1,CDom); //Remove members that the new C dominates from the archive
				
				TStp1 = Utility.union(TStp1, Tsel); //Add the useful tests to the archive
				Tnew = Utility.remove(Tnew,Tsel); //remove the useful tests from the list of new tests
			}
			
		}
		if(! Utility.isEqualTo(CStp1, CSt)){ //If a change has been made to the list of candidates
			t = t+1;
			CSt = Utility.copy(CStp1); TSt = Utility.copy(TStp1);
		}
		
		return t;
	}
	
	public static Vector findUseful(Genotype C, ArrayList<Genotype> Cset, ArrayList<Genotype> Tset, ArrayList<Genotype> Tnew){
		
		boolean useful = true;
		ArrayList<Genotype> Tsel = new ArrayList();
		ArrayList<Genotype> Trest = Utility.copy(Tnew);
		
		//System.out.println("New tests: "+Trest);
		
		Iterator<Genotype> k1 = Cset.iterator();
		while(k1.hasNext() && useful){
			Genotype Cprime = k1.next();
			//System.out.println("Cprime: "+Cprime);
			
			if(greaterOrEqualOnAllTest(Cprime,C,Utility.union(Tset, Tsel))){
				boolean found = false;
				
				Iterator<Genotype> k2 = Trest.iterator();
				while(k2.hasNext()&&!found){
					Genotype T = k2.next();
					if(G(C,T)>G(Cprime,T)){
						Tsel = Utility.union(Tsel, T);
						Trest = Utility.remove(T,Trest);
						found = true;
					}	
				}
				
				if(!found){
					useful = false;
				}
			}
		}
		
		Vector v = new Vector(); v.add((Boolean)useful); v.add(Tsel);
		return v;
	}
	
	public static boolean greaterOrEqualOnAllTest(Genotype C, Genotype Cprime, ArrayList<Genotype> T){
		for(Genotype t : T){
			int x = G(Cprime,t); int y = G(C,t);
			if(x>y){
				
				//System.out.println("Returned false, because: "+x+" is greater than "+y+" for t "+t);
				
				return false;
			}
		}
		return true;
	}
	
	public static ArrayList<Genotype> dominated(Genotype C, ArrayList<Genotype> candidates,ArrayList<Genotype> tests){
		
		ArrayList<Genotype> dominatedCandidates  = new ArrayList<Genotype>();
		
		int i=0; int j=0;
		boolean[][] outcomes = new boolean[candidates.size()][tests.size()];
		boolean[] Coutcomes = new boolean[tests.size()];
		
		for(Genotype c : candidates){
			for(Genotype t : tests){
				outcomes[i][j] = GB(c,t);
				//System.out.println("outcomes: "+i+" "+j+" "+outcomes[i][j]);
				j++;
			}
			j = 0;
			i++;
		}
		
		i=0;
		for(Genotype t : tests){
			Coutcomes[i] = GB(C,t);
			//System.out.println("Coutcomes: "+i+" "+Coutcomes[i]);
			i++;
		}
		
		i=0;
		for(Genotype c : candidates){
			j=0;
			boolean atLeastAsGood = true; boolean betterInOne = false; //these values refer to relation with C over the test set.
			while(j<tests.size() && atLeastAsGood){
				
				boolean a = outcomes[i][j]; boolean b = Coutcomes[j];
				
				if(a && !b){ //the member of candidates is able to solve a test which C cannot.
					
					//System.out.println("Candidate "+c+" solves the test "+tests.get(j)+" which is not solved by the dominater");
					atLeastAsGood = false;
				}
				
				if(!a && b){ //C is able to solve a test which the member cannot 
					//System.out.println("Candidate "+candidates.get(i)+" cannot solve the test "+tests.get(j)+" and the dominator can");
					betterInOne = true;
				}
				j++;
			}
			if(betterInOne && atLeastAsGood){
				dominatedCandidates.add(c);
			}
			i++;
		}
		return dominatedCandidates;
	}
	
	public static boolean GB(Genotype g1, Genotype g2){
		int x = G(g1,g2);
		if(x==1){return true;} else {return false;}
	}
	
	public static int G(Genotype g1, Genotype g2){ //currently this needs changing to reference the correct generator
		
		double[] d;
		
		if(SymbolicCGPGenerator.POP1.contains(g1) && SymbolicCGPGenerator.POP2.contains(g2)){		
			d = SymbolicCGPGenerator.evaluate(g1,g2);
		
		}
		else {
					
			d = SymbolicCGPGenerator.evaluate(g1,g2);			
			
		}
		
		return (int)d[0];
	}

	public static void clear(){
		CSt = new ArrayList<Genotype>();
		TSt = new ArrayList<Genotype>();
	}
	
	
	
	public static ArrayList<Genotype> getCSt() {
		return CSt;
	}

	public static void setCSt(ArrayList<Genotype> cSt) {
		CSt = cSt;
	}

	public static ArrayList<Genotype> getTSt() {
		return TSt;
	}

	public static void setTSt(ArrayList<Genotype> tSt) {
		TSt = tSt;
	}
	
	public static void printArchive(){
		System.out.println("Candidates: ");
		Utility.printPop(CSt);
		System.out.println("Tests: ");
		Utility.printPop(TSt);
	}

	public static void main(String args[]){
		//Test framework for IPCA archive methods
		
		//Configure sample set of populations
		ArrayList<Genotype> p1 = new ArrayList<Genotype>();
		ArrayList<Genotype> p2 = new ArrayList<Genotype>();
		ArrayList<Genotype> p3 = new ArrayList<Genotype>();
		ArrayList<Genotype> p4 = new ArrayList<Genotype>();
		
		double[] in = {0.0,1.0,0.0};
		for(int i=0;i<3;i++){
			p1.add(new GGT(in,5,5,5));
			p2.add(new GGT(in,5,5,5));
			p3.add(new GGT(in,5,5,5));
			p4.add(new GGT(in,5,5,5));
		}
		Utility.printPop(p1);
		Utility.printPop(p2);
		Utility.printPop(p3);
		Utility.printPop(p4);
		
		for(int i=0;i<3;i++){
			for(int j=0;j<3;j++){
				System.out.println(p1.get(i)+" "+p2.get(j)+" "+G(p1.get(i),p2.get(j)));
			}
		}
		
		//Testing GB method
		System.out.println("---GB method test---");
		
		for(int i=0;i<3;i++){
			for(int j=0;j<3;j++){
				System.out.println(p1.get(i)+" "+p2.get(j)+" "+GB(p1.get(i),p2.get(j)));
			}
		}
		
		/*//Testing greaterOrEqualOnAll
		System.out.println("---Greater Or Equal Test---");
		GGT g1 = (GGT) p1.get(0); System.out.println(g1);
		GGT g2 = (GGT) p1.get(1); System.out.println(g2);
		System.out.println("The statement g1 is greater than or equal to g2 on all tests is : "+greaterOrEqualOnAllTest(g1,g2,p2));*/
		
		
		//Testing dominated by
		/*System.out.println("---Dominated by Test---");
		GGT g1 = (GGT) p1.get(0); System.out.println(g1+" dominates the following candidates from p1: ");
		ArrayList<Genotype> d = dominated(g1,p1,p2);
		Utility.printPop(d);*/
		
		//Testing find useful
		
		ArrayList<Genotype> archivedCandidates = new ArrayList<Genotype>();
		ArrayList<Genotype> archivedTests = new ArrayList<Genotype>();
		
		for(int i=0;i<p1.size();i++){
			System.out.println("---Find Useful Test "+i+" ---");
			GGT g1 = (GGT) p1.get(i); System.out.println("Candidate C: "+g1);
			Vector v = findUseful(g1,archivedCandidates,archivedTests, p2);
			//arguments to find useful, test on g1, g2, g3 etc.
			boolean b = (Boolean)v.get(0);
			ArrayList<Genotype> usefulTests = (ArrayList<Genotype>)v.get(1);
			if(b){archivedCandidates = Utility.union(archivedCandidates, g1); archivedTests = Utility.union(archivedTests, usefulTests);}
			System.out.println("Useful is "+b);
			System.out.println("Useful tests: "+usefulTests);
			System.out.println("Archive candidates: "+archivedCandidates);
		}
	}
}
